#include <functional>

namespace ptr
{
    template <class T>
    class shared_ptr
    {
        using deleter = function<void(T *)>;

    public:
        shared_ptr<T>(T *data)
            : _data(data), _count(new std::atomic<int>(1))
        {
        }

        template <class Del>
        shared_ptr<T>(T *data, Del del)
            : _data(data), _count(new std::atomic<int>(1)), _del(del)
        {
        }

        shared_ptr<T>(const shared_ptr<T> &tmp)
        {
            _data = tmp._data;
            _count = tmp._count;
            _del = tmp._del;
            *(_count) += 1;
        }

        void release()
        {
            if (--(*_count) == 0)
            {
                _del(_data);
                delete _count;
                _data = nullptr;
                _count = nullptr;
            }
        }

        shared_ptr<T> &operator=(const shared_ptr<T> &tmp)
        {
            if (this != &tmp)
            {
                // 前后资源不同
                release();
                _data = tmp._data;
                _count = tmp._count;
                _del = tmp._del;
                *(_count) += 1;
            }
            return *this;
        }

        T *get() const
        {
            return _data;
        }

        int use_count() const
        {
            return *_count;
        }

        T &operator*()
        {
            return *_data;
        }

        T *operator->()
        {
            return _data;
        }

        ~shared_ptr()
        {
            release();
        }

    private:
        T *_data;
        std::atomic<int> *_count;
        deleter _del = [](T *ptr)
        { delete ptr; };
    };

    template <class T>
    class unique_ptr
    {
    public:
        unique_ptr<T>(T *data)
            : _data(data)
        {
        }

        unique_ptr<T>(const unique_ptr<T> &tmp) = delete;

        unique_ptr<T> &operator=(const unique_ptr<T> &tmp) = delete;

        T &operator*()
        {
            return *_data;
        }

        T *operator->()
        {
            return _data;
        }

        ~unique_ptr()
        {
            if (_data)
                delete _data;
        }

        unique_ptr<T> &operator=(unique_ptr<T> &&sp)
        {
            if (this != &sp)
            {
                delete _data;
                _data = sp._data;
                sp._data = nullptr;
            }
            return *this;
        }

    private:
        T *_data;
    };
}
